home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 #2 / Ham Radio 2000 - Volume 2.iso / HAMV2 / MISC / DTMFF110 / SBIO.C < prev    next >
C/C++ Source or Header  |  1997-08-05  |  14KB  |  569 lines

  1. /* SBIO.C  */
  2. /* Soundblaster 16 basic audio I/O functions */
  3. /* Copyright 1995 by Ethan Brodsky.  All rights reserved */
  4. /* Modified extensively by Philip VanBaren to suit my purposes */
  5. /* Modified by Emil Laurentiu Wednesday, 06 August 1997 */
  6.  
  7. /* Interface variables that can be changed in the background */
  8. volatile int  sb16dmarunning;
  9. volatile char curblock;
  10.  
  11. #include <alloc.h>
  12. #include <stdio.h>
  13. #include <dos.h>
  14. #include <mem.h>
  15. #include <stdlib.h>
  16. #include "sbio.h"
  17. #include "freq.h"               /* Needed for DOUT() definition only */
  18.  
  19. #define lo(value) (unsigned char)((value) & 0x00FF)
  20. #define hi(value) (unsigned char)((value) >> 8)
  21.  
  22. int          mixerport;
  23. int          mixdataport;
  24. int          resetport;
  25. int          readport;
  26. int          writeport;
  27. int          pollport;
  28. int          poll16port;
  29.  
  30. int          pic_rotateport;
  31. int          pic_maskport;
  32.  
  33. int          dma_maskport;
  34. int          dma_clrptrport;
  35. int          dma_modeport;
  36. int          dma_baseaddrport;
  37. int          dma_countport;
  38. int          dma_pageport;
  39.  
  40. char          irq_startmask;
  41. char          irq_stopmask;
  42. char          irq_intvector;
  43. char          int_controller;
  44.  
  45. char          dma_startmask;
  46. char          dma_stopmask;
  47. char          dma_mode;
  48.  
  49. /* This function is defined in sc_sb16.c */
  50. extern void interrupt sb16_callback( void );
  51. void
  52. interrupt( *oldintvector ) (  ) = NULL;
  53. int          handlerinstalled;
  54.  
  55. void far     *dmabuffer = NULL; /* Twice the size of the output buffer */
  56. int far         *dmaptr = NULL;    /* Pointer to the used portion */
  57.  
  58. unsigned long buf_addr;        /* 16-bit addressing */
  59. unsigned char buf_page;
  60. unsigned int  buf_ofs;
  61.  
  62. mode          iomode;        /* Flags input or output mode */
  63.  
  64. /* Low level sound card I/O  */
  65. void
  66. write_dsp( unsigned char value )
  67. {
  68.   while ( inp( writeport ) & 0x80 );    /* Wait for bit 7 to be cleared */
  69.   outp( writeport, value );
  70. }
  71.  
  72. unsigned char
  73. read_dsp( void )
  74. {
  75.   long        timeout = 1000000L;
  76.   /* Wait for bit 7 to be set, or for a timeout */
  77.   while ( ( !( inp( pollport ) & 0x80 ) ) && ( --timeout ) );
  78.   return inp( readport );
  79. }
  80.  
  81. int
  82. reset_dsp( void )
  83. {
  84.   int        i;
  85.  
  86.   sb16dmarunning = 0;
  87.   outp( resetport, 1 );
  88.   outp( resetport, 0 );
  89.   i = 100;
  90.  
  91.   while ( ( read_dsp(  ) != 0xAA ) && i-- );
  92.   return i;
  93. }
  94.  
  95. /* Convert a far pointer to a linear address, for DMA addressing */
  96. #define getlinearaddr(p) ((unsigned long)FP_SEG(p)*16 + (unsigned long)FP_OFF(p))
  97.  
  98. /* Initialization and shutdown    */
  99. void
  100. installhandler( void )
  101. {
  102.   /* Install the interrupt handler */
  103.   disable(  );            /* Disable interrupts  */
  104.   outp( pic_maskport, ( inp( pic_maskport ) | irq_stopmask ) ); /* Mask IRQ    */
  105.   oldintvector = getvect( irq_intvector );    /* Save old vector     */
  106.   setvect( irq_intvector, sb16_callback );    /* Install new handler */
  107.   outp( pic_maskport, ( inp( pic_maskport ) & irq_startmask ) );    /* Unmask IRQ  */
  108.   enable(  );            /* Reenable interupts  */
  109.   handlerinstalled = 1;
  110. }
  111. void
  112. uninstallhandler( void )
  113. {
  114.   sb16dmarunning = 0;
  115.   disable(  );            /* Disable interrupts  */
  116.   outp( pic_maskport, ( inp( pic_maskport ) | irq_stopmask ) ); /* Mask IRQ     */
  117.   setvect( irq_intvector, oldintvector );    /* Restore old vector  */
  118.   enable(  );            /* Enable interrupts   */
  119.   handlerinstalled = 0;
  120. }
  121.  
  122. /* This function is run at program exit to ensure cleanup */
  123. void
  124. sb_exitproc( void )
  125. {
  126.   sb16dmarunning = 0;
  127.   outp( 0x20, 0x20 );
  128.   outp( 0xA0, 0x20 );        /* Acknowledge any hanging ints */
  129.   write_dsp( 0xD5 );        /* Stop digitized sound xfer     */
  130.   outp( dma_maskport, dma_stopmask );    /* Mask DMA channel         */
  131.   if ( handlerinstalled )
  132.     uninstallhandler(  );    /* Uninstall int handler    */
  133.   reset_dsp(  );        /* Reset SB DSP             */
  134. }
  135.  
  136. /*
  137.  * Initialize the Soundblaster-16 card with the settings:
  138.  * baseio: base IO address for the card
  139.  *    irq: IRQ channel used by the card
  140.  *  dma16: 16-bit DMA channel used by the card
  141.  *     io: sampling mode, either "input" or "output"
  142.  * length: maximum block size to be used, in samples
  143.  *       (this is used for allocating the DMA buffer)
  144.  */
  145. int
  146. init_sb( int baseio, char irq, char dma16, mode io, unsigned int length )
  147. {
  148.   int        val, onboard;
  149.  
  150.   /* Sound card IO ports */
  151.   mixerport = baseio + 0x004;
  152.   mixdataport = baseio + 0x005;
  153.   resetport = baseio + 0x006;
  154.   readport = baseio + 0x00A;
  155.   writeport = baseio + 0x00C;
  156.   pollport = baseio + 0x00E;
  157.   poll16port = baseio + 0x00F;
  158.  
  159.   /* Reset DSP */
  160.   sb16dmarunning = 0;
  161.   if ( !reset_dsp(  ) )
  162.     return 0;
  163.  
  164.   /* Verify that we have a SB-16 at this address */
  165.   write_dsp( 0xE1 );
  166.   val = read_dsp(  );        /* Get the major version number */
  167.   read_dsp(  );            /* Grab the minor version number also */
  168.   if ( val < 4 )
  169.     return 0;
  170.  
  171.   /*
  172.    * Check the current IRQ and DMA settings, and compare with the values
  173.    * passed to this routine
  174.    */
  175.   outportb( mixerport, 0x80 );    /* IRQ settings */
  176.   val = inportb( mixdataport );
  177.   onboard = -1;
  178.   if ( val & 0x01 )
  179.   {
  180.     onboard = 2;
  181.     DOUT( "SB16: IRQ2 enabled" );
  182.   }
  183.   if ( val & 0x02 )
  184.   {
  185.     onboard = 5;
  186.     DOUT( "SB16: IRQ5 enabled" );
  187.   }
  188.   if ( val & 0x04 )
  189.   {
  190.     onboard = 7;
  191.     DOUT( "SB16: IRQ7 enabled" );
  192.   }
  193.   if ( val & 0x08 )
  194.   {
  195.     onboard = 10;
  196.     DOUT( "SB16: IRQ10 enabled" );
  197.   }
  198.   if ( onboard == -1 )
  199.   {
  200.     puts( "Error: Soundblaster has no IRQ enabled, aborting ..." );
  201.     return ( 0 );
  202.   }
  203.   switch ( irq )
  204.   {
  205.     case 2:
  206.       if ( !( val & 0x01 ) )
  207.       {
  208.     irq = -1;
  209.       } break;
  210.     case 5:
  211.       if ( !( val & 0x02 ) )
  212.       {
  213.     irq = -1;
  214.       } break;
  215.     case 7:
  216.       if ( !( val & 0x04 ) )
  217.       {
  218.     irq = -1;
  219.       } break;
  220.     case 10:
  221.       if ( !( val & 0x08 ) )
  222.       {
  223.     irq = -1;
  224.       } break;
  225.     default:
  226.       irq = -1;
  227.   }
  228.   if ( irq == -1 )
  229.   {
  230.     printf( "Warning: Soundblaster has IRQ%d selected, using that value instead\n", onboard );
  231.     irq = onboard;
  232.   }
  233.   outportb( mixerport, 0x81 );    /* DMA settings */
  234.   val = inportb( mixdataport );
  235.   onboard = -1;
  236.   if ( val & 0x01 )
  237.   {                /* onboard=0; */
  238.     DOUT( "SB16: DMA0 enabled" );
  239.   }
  240.   if ( val & 0x02 )
  241.   {                /* onboard=1; */
  242.     DOUT( "SB16: DMA1 enabled" );
  243.   }
  244.   if ( val & 0x08 )
  245.   {                /* onboard=3; */
  246.     DOUT( "SB16: DMA3 enabled" );
  247.   }
  248.   if ( val & 0x20 )
  249.   {
  250.     onboard = 5;
  251.     DOUT( "SB16: DMA5 enabled" );
  252.   }
  253.   if ( val & 0x40 )
  254.   {
  255.     onboard = 6;
  256.     DOUT( "SB16: DMA6 enabled" );
  257.   }
  258.   if ( val & 0x80 )
  259.   {
  260.     onboard = 7;
  261.     DOUT( "SB16: DMA7 enabled" );
  262.   }
  263.   if ( onboard == -1 )
  264.   {
  265.     puts( "Error: Soundblaster has no 16-bit DMA enabled, aborting ..." );
  266.     return ( 0 );
  267.   }
  268.   switch ( dma16 )
  269.   {
  270. //    case 0:     if(!(val&0x01)) { dma16=-1; } break;
  271. //    case 1:     if(!(val&0x02)) { dma16=-1; } break;
  272. //    case 3:     if(!(val&0x08)) { dma16=-1; } break;
  273.     case 5:
  274.       if ( !( val & 0x20 ) )
  275.       {
  276.     dma16 = -1;
  277.       } break;
  278.     case 6:
  279.       if ( !( val & 0x40 ) )
  280.       {
  281.     dma16 = -1;
  282.       } break;
  283.     case 7:
  284.       if ( !( val & 0x80 ) )
  285.       {
  286.     dma16 = -1;
  287.       } break;
  288.     default:
  289.       dma16 = -1;
  290.   }
  291.   if ( dma16 == -1 )
  292.   {
  293.     printf( "Warning: Soundblaster has DMA%d selected, using that value instead\n", onboard );
  294.     dma16 = onboard;
  295.   }
  296.  
  297. #ifdef DEBUG_OUTPUT
  298.   /* Check if the IRQs are enabled */
  299.   outportb( mixerport, 0x82 );    /* IRQ status */
  300.   val = inportb( mixdataport );
  301.   if ( val & 0x01 )
  302.   {
  303.     DOUT( "SB16: 8-bit IRQ active" );
  304.   }
  305.   if ( val & 0x02 )
  306.   {
  307.     DOUT( "SB16: 16-bit IRQ active" );
  308.   }
  309.   if ( val & 0x04 )
  310.   {
  311.     DOUT( "SB16: MPU-401 IRQ active" );
  312.   }
  313. #endif
  314.   /* These two lines are strictly a kludge for freq.exe */
  315.   outportb( mixerport, 0x3d );    /* Input control */
  316.   outportb( mixdataport, 0x7f );/* Use all channels for input */
  317.  
  318.   /* Compute interrupt ports and parameters */
  319.   if ( irq < 8 )
  320.   {
  321.     int_controller = 1;
  322.     pic_rotateport = 0x20;
  323.     pic_maskport = 0x21;
  324.     irq_intvector = 0x08 + irq;
  325.   }
  326.   else
  327.   {
  328.     int_controller = 2;
  329.     pic_rotateport = 0xA0;
  330.     pic_maskport = 0x21;
  331.     irq_intvector = 0x70 + irq - 8;
  332.   }
  333.   irq_stopmask = 1 << ( irq % 8 );
  334.   irq_startmask = ~irq_stopmask;
  335.  
  336.   /* Compute DMA ports and parameters */
  337.   dma_maskport = 0xD4;
  338.   dma_clrptrport = 0xD8;
  339.   dma_modeport = 0xD6;
  340.   dma_baseaddrport = 0xC0 + 4 * ( dma16 - 4 );
  341.   dma_countport = 0xC2 + 4 * ( dma16 - 4 );
  342.  
  343.   switch ( dma16 )
  344.   {
  345.     case 5:
  346.       dma_pageport = 0x8B;
  347.       break;
  348.     case 6:
  349.       dma_pageport = 0x89;
  350.       break;
  351.     case 7:
  352.       dma_pageport = 0x8A;
  353.       break;
  354.   }
  355.  
  356.   dma_stopmask = dma16 - 4 + 0x04;    /* 000001xx */
  357.   dma_startmask = dma16 - 4 + 0x00;    /* 000000xx */
  358.  
  359.   /* Allocate a buffer for DMA transfer */
  360.   /* (need a block of memory that does not cross a page boundary) */
  361.   if ( ( dmabuffer = farmalloc( 8 * length ) ) == NULL )
  362.   {
  363.     puts( "Unable to allocate DMA buffer." );
  364.     exit( 1 );
  365.   }
  366.   dmaptr = ( int far * ) dmabuffer;
  367.   if ( ( ( getlinearaddr( dmabuffer ) >> 1 ) % 65536L ) + length * 2 > 65536L )
  368.     dmaptr += 2 * length;    /* Pick second half to avoid crossing
  369.                  * boundary */
  370.  
  371.   /* Compute DMA parameters */
  372.   buf_addr = getlinearaddr( dmaptr );
  373.   buf_page = ( unsigned int ) ( buf_addr >> 16 );
  374.   buf_ofs = ( unsigned int ) ( ( buf_addr >> 1 ) % 65536L );
  375.  
  376.   /* Other initialization */
  377.   iomode = io;
  378.   switch ( iomode )
  379.   {
  380.     case input:
  381.       dma_mode = dma16 - 4 + 0x54;
  382.       break;            /* 010101xx */
  383.     case output:
  384.       dma_mode = dma16 - 4 + 0x58;
  385.       break;            /* 010110xx */
  386.   }
  387.  
  388.   installhandler(  );        /* Install interrupt handler */
  389.   atexit( sb_exitproc );    /* Install exit procedure    */
  390.  
  391.   return 1;
  392. }
  393.  
  394. /*
  395.  * Shut down the sampling process, clean up the interrupts,
  396.  * and free up the memory allocation
  397.  */
  398. void
  399. shutdown_sb( void )
  400. {
  401.   sb16dmarunning = 0;
  402.   reset_dsp(  );
  403.   if ( handlerinstalled )
  404.     uninstallhandler(  );
  405.   farfree( dmabuffer );
  406. }
  407.  
  408. /*
  409.  * Start continuous I/O at a rate of {rate} Hz, with a call to the
  410.  * callback_sb16 procedure after every block of length {length} has
  411.  * been finished.  The sampling input or output is done on alternating
  412.  * blocks pointed to by (dmaptr+curblock*length).
  413.  *  i.e. When curblock is 0, the current block is (dmaptr).
  414.  *     When curblock is 1, the current block is (dmaptr+length)
  415.  * The variable curblock is maintained in the callback_sb16 routine.
  416.  */
  417. void
  418. startio( unsigned int rate, unsigned long length )
  419. {
  420.   sb16dmarunning = 1;
  421.   curblock = 0;
  422.  
  423.   /* Program DMA controller */
  424.   outp( dma_maskport, dma_stopmask );
  425.   outp( dma_clrptrport, 0x00 );
  426.   outp( dma_modeport, dma_mode );
  427.   outp( dma_baseaddrport, lo( buf_ofs ) );    /* Low byte of offset  */
  428.   outp( dma_baseaddrport, hi( buf_ofs ) );    /* High word of offset */
  429.   outp( dma_countport, lo( length * 2 - 1 ) );    /* Low byte of count   */
  430.   outp( dma_countport, hi( length * 2 - 1 ) );    /* High byte of count  */
  431.   outp( dma_pageport, buf_page );
  432.   outp( dma_maskport, dma_startmask );
  433.  
  434.   /* Program sound card */
  435.   switch ( iomode )
  436.   {
  437.     case input:
  438.       write_dsp( 0x42 );
  439.       break;            /* Set input sampling rate  */
  440.     case output:
  441.       write_dsp( 0x41 );
  442.       break;            /* Set output sampling rate */
  443.   }
  444.   write_dsp( hi( rate ) );    /* High byte of sampling rate */
  445.   write_dsp( lo( rate ) );    /* Low byte of sampling rate  */
  446.   switch ( iomode )
  447.   {
  448.     case output:
  449.       write_dsp( 0xB6 );
  450.       break;            /* 16-bit D->A, A/I, FIFO   */
  451.     case input:
  452.       write_dsp( 0xBE );
  453.       break;            /* 16-bit A->D, A/I, FIFO   */
  454.   }
  455.   write_dsp( 0x10 );        /* DMA Mode:  16-bit signed mono */
  456.   write_dsp( lo( length - 1 ) );/* Low byte of block length     */
  457.   write_dsp( hi( length - 1 ) );/* High byte of block length     */
  458. }
  459.  
  460. /*
  461.  * Stops the current sampling process.
  462.  */
  463. void
  464. stopio( void )
  465. {
  466.   outp( 0x20, 0x20 );
  467.   outp( 0xA0, 0x20 );        /* Acknowledge any hanging ints */
  468.   write_dsp( 0xD9 );        /* Stop digitized sound xfer     */
  469.   outp( dma_maskport, dma_stopmask );    /* Mask DMA channel         */
  470.   sb16dmarunning = 0;
  471.   reset_dsp(  );        /* Reset SB DSP             */
  472. }
  473.  
  474. /* Mixer setting and reading functions */
  475. void
  476. set_cd_level( unsigned int level )
  477. {
  478.   level = level * 256 / 100;
  479.   if ( level > 255 )
  480.     level = 255;
  481.   outportb( mixerport, 0x36 );    /* CD Audio left */
  482.   outportb( mixdataport, level );
  483.   outportb( mixerport, 0x37 );    /* CD Audio right */
  484.   outportb( mixdataport, level );
  485. }
  486.  
  487. void
  488. set_mic_level( unsigned int level )
  489. {
  490.   level = level * 256 / 100;
  491.   if ( level > 255 )
  492.     level = 255;
  493.   outportb( mixerport, 0x3A );    /* Microphone */
  494.   outportb( mixdataport, level );
  495. }
  496.  
  497. void
  498. set_line_level( unsigned int level )
  499. {
  500.   level = level * 256 / 100;
  501.   if ( level > 255 )
  502.     level = 255;
  503.   outportb( mixerport, 0x38 );    /* Line In left */
  504.   outportb( mixdataport, level );
  505.   outportb( mixerport, 0x39 );    /* Line In right */
  506.   outportb( mixdataport, level );
  507. }
  508.  
  509. unsigned int
  510. get_cd_level( void )
  511. {
  512.   unsigned int    level;
  513.   outportb( mixerport, 0x36 );    /* CD Audio left */
  514.   level = inportb( mixdataport );
  515.   outportb( mixerport, 0x37 );    /* CD Audio right */
  516.   level += inportb( mixdataport );
  517.   level = level * 50 / 256;
  518.   if ( level > 100 )
  519.     level = 100;
  520.   return ( level );
  521. }
  522.  
  523. unsigned int
  524. get_mic_level( void )
  525. {
  526.   unsigned int    level;
  527.   outportb( mixerport, 0x3A );    /* Microphone */
  528.   level = inportb( mixdataport );
  529.   level = level * 100 / 256;
  530.   if ( level > 100 )
  531.     level = 100;
  532.   return ( level );
  533. }
  534.  
  535. unsigned int
  536. get_line_level( void )
  537. {
  538.   unsigned int    level;
  539.   outportb( mixerport, 0x38 );    /* Line In left */
  540.   level = inportb( mixdataport );
  541.   outportb( mixerport, 0x39 );    /* Line In right */
  542.   level += inportb( mixdataport );
  543.   level = level * 50 / 256;
  544.   if ( level > 100 )
  545.     level = 100;
  546.   return ( level );
  547. }
  548.  
  549. void
  550. set_master_level( int level )
  551. {
  552.   level &= 0xf;
  553.   level |= level << 4;
  554.   outportb( sb_addr + 0x4, 0x22 );  /* Master volume */
  555.   outportb( sb_addr + 0x5, level );
  556.   return;
  557. }
  558.  
  559. void
  560. set_fm_level( int level )
  561. {
  562.   level &= 0xf;
  563.   level |= level << 4;
  564.   outportb( sb_addr + 0x4, 0x26 );  /* FM volume */
  565.   outportb( sb_addr + 0x5, level );
  566.   return;
  567. }
  568.  
  569.